<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>M3U播放列表在线管理工具</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/hls.js@1.4.12/dist/hls.min.js"></script>
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3B82F6',
                        secondary: '#10B981',
                        danger: '#EF4444',
                        dark: '#1E293B',
                        light: '#F8FAFC'
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .shadow-soft {
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
            }
            .transition-all-300 {
                transition: all 300ms ease-in-out;
            }
            .scrollbar-thin {
                scrollbar-width: thin;
            }
            .scrollbar-thin::-webkit-scrollbar {
                width: 4px;
                height: 4px;
            }
            .scrollbar-thin::-webkit-scrollbar-thumb {
                background-color: rgba(156, 163, 175, 0.5);
                border-radius: 2px;
            }
            .group-header {
                background-color: rgba(59, 130, 246, 0.1);
            }
            .channel-item:hover {
                background-color: rgba(59, 130, 246, 0.05);
            }
        }
    </style>
</head>
<body class="bg-gray-50 min-h-screen font-sans text-dark">
    <!-- 顶部导航 -->
    <header class="bg-white shadow-md">
        <div class="container mx-auto px-4 py-4 flex justify-between items-center">
            <div class="flex items-center space-x-2">
                <i class="fa fa-play-circle text-primary text-2xl"></i>
                <h1 class="text-xl md:text-2xl font-bold text-primary">M3U播放列表管理工具</h1>
            </div>
            <div class="hidden md:flex items-center space-x-6">
                <a href="#" class="text-gray-600 hover:text-primary transition-all-300">
                    <i class="fa fa-question-circle mr-1"></i>帮助
                </a>
                <a href="#" class="text-gray-600 hover:text-primary transition-all-300">
                    <i class="fa fa-info-circle mr-1"></i>关于
                </a>
            </div>
        </div>
    </header>

    <!-- 主内容区 -->
    <main class="container mx-auto px-4 py-8">
        <div class="max-w-5xl mx-auto">
            <!-- 介绍卡片 -->
            <div class="bg-white rounded-lg shadow-soft p-6 mb-8">
                <h2 class="text-xl font-semibold mb-3 flex items-center">
                    <i class="fa fa-info-circle text-primary mr-2"></i>工具介绍
                </h2>
                <p class="text-gray-600 mb-4">
                    这个工具可以帮助您管理M3U播放列表，支持多文件合并、重复地址检测、无效地址修复、在线预览以及频道分组功能，让您的播放列表更加有序。
                </p>
                <div class="grid md:grid-cols-3 gap-4 text-sm">
                    <div class="flex items-start">
                        <i class="fa fa-files-o text-primary mt-1 mr-2"></i>
                        <div>
                            <p class="font-medium">多文件合并与管理</p>
                            <p class="text-gray-500">合并多个M3U文件并智能处理</p>
                        </div>
                    </div>
                    <div class="flex items-start">
                        <i class="fa fa-check-circle text-secondary mt-1 mr-2"></i>
                        <div>
                            <p class="font-medium">有效性与重复检测</p>
                            <p class="text-gray-500">自动识别并处理无效和重复地址</p>
                        </div>
                    </div>
                    <div class="flex items-start">
                        <i class="fa fa-folder-open text-primary mt-1 mr-2"></i>
                        <div>
                            <p class="font-medium">频道分组管理</p>
                            <p class="text-gray-500">自动与手动分组，便于查找</p>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 上传区域 -->
            <div class="bg-white rounded-lg shadow-soft p-6 mb-8">
                <h2 class="text-xl font-semibold mb-4 flex items-center">
                    <i class="fa fa-upload text-primary mr-2"></i>上传M3U文件
                </h2>
                
                <div id="dropArea" class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center hover:border-primary transition-all-300 cursor-pointer mb-4">
                    <i class="fa fa-file-text-o text-4xl text-gray-400 mb-3"></i>
                    <p class="text-gray-600 mb-2">拖放一个或多个M3U文件到这里，或</p>
                    <label class="inline-block bg-primary text-white py-2 px-4 rounded-md hover:bg-primary/90 transition-all-300 cursor-pointer">
                        <i class="fa fa-folder-open mr-1"></i>选择文件
                        <input type="file" id="fileInput" accept=".m3u,.m3u8" class="hidden" multiple>
                    </label>
                    <p class="text-xs text-gray-500 mt-3">支持 .m3u 和 .m3u8 格式文件，可上传多个</p>
                </div>
                
                <div id="filesList" class="hidden mb-4 max-h-40 overflow-y-auto scrollbar-thin">
                    <div class="flex items-center justify-between border-b border-gray-100 pb-2 mb-2">
                        <div class="font-medium">已选择文件:</div>
                        <button id="removeAllFiles" class="text-sm text-gray-500 hover:text-danger transition-all-300">
                            <i class="fa fa-trash-o mr-1"></i>清空
                        </button>
                    </div>
                    <ul id="selectedFiles" class="space-y-2">
                        <!-- 选中的文件会显示在这里 -->
                    </ul>
                </div>
                
                <!-- 选项设置 -->
                <div class="mb-6">
                    <h3 class="font-medium mb-3">处理选项</h3>
                    <div class="grid md:grid-cols-3 gap-4">
                        <div>
                            <label for="timeout" class="block text-sm text-gray-600 mb-1">超时时间 (秒)</label>
                            <input type="number" id="timeout" value="10" min="1" max="30" 
                                class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50">
                        </div>
                        <div>
                            <label for="retry" class="block text-sm text-gray-600 mb-1">重试次数</label>
                            <input type="number" id="retry" value="2" min="1" max="5" 
                                class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50">
                        </div>
                        <div>
                            <label class="block text-sm text-gray-600 mb-1">重复处理</label>
                            <div class="flex items-center space-x-4">
                                <label class="inline-flex items-center">
                                    <input type="radio" name="duplicateAction" value="keepFirst" checked class="form-radio text-primary">
                                    <span class="ml-2">保留第一个</span>
                                </label>
                                <label class="inline-flex items-center">
                                    <input type="radio" name="duplicateAction" value="keepLast" class="form-radio text-primary">
                                    <span class="ml-2">保留最后一个</span>
                                </label>
                            </div>
                        </div>
                    </div>
                    
                    <!-- 分组选项 -->
                    <div class="mt-4">
                        <label class="block text-sm text-gray-600 mb-2">自动分组方式</label>
                        <div class="grid md:grid-cols-3 gap-4">
                            <label class="inline-flex items-center p-3 border border-gray-200 rounded-md cursor-pointer hover:border-primary transition-all-300">
                                <input type="radio" name="groupingMethod" value="channelType" checked class="form-radio text-primary">
                                <span class="ml-2">按频道类型（电影/体育等）</span>
                            </label>
                            <label class="inline-flex items-center p-3 border border-gray-200 rounded-md cursor-pointer hover:border-primary transition-all-300">
                                <input type="radio" name="groupingMethod" value="sourceFile" class="form-radio text-primary">
                                <span class="ml-2">按源文件</span>
                            </label>
                            <label class="inline-flex items-center p-3 border border-gray-200 rounded-md cursor-pointer hover:border-primary transition-all-300">
                                <input type="radio" name="groupingMethod" value="none" class="form-radio text-primary">
                                <span class="ml-2">不自动分组</span>
                            </label>
                        </div>
                    </div>
                </div>
                
                <button id="processBtn" class="w-full bg-primary text-white py-3 px-4 rounded-md hover:bg-primary/90 transition-all-300 font-medium disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center">
                    <i class="fa fa-cog mr-2"></i>处理并合并文件
                </button>
            </div>

            <!-- 处理进度 -->
            <div id="progressSection" class="hidden bg-white rounded-lg shadow-soft p-6 mb-8">
                <h2 class="text-xl font-semibold mb-4 flex items-center">
                    <i class="fa fa-spinner fa-spin text-primary mr-2"></i>处理进度
                </h2>
                
                <div class="mb-4">
                    <div class="flex justify-between text-sm mb-1">
                        <span id="progressText">准备开始...</span>
                        <span id="progressPercent">0%</span>
                    </div>
                    <div class="w-full bg-gray-200 rounded-full h-2.5">
                        <div id="progressBar" class="bg-primary h-2.5 rounded-full w-0 transition-all duration-300"></div>
                    </div>
                </div>
                
                <div id="logContainer" class="bg-gray-50 rounded-md p-4 h-40 overflow-y-auto text-sm font-mono text-gray-700 scrollbar-thin">
                    <div id="processLog"></div>
                </div>
            </div>

            <!-- 结果和预览区域 -->
            <div id="resultsSection" class="hidden bg-white rounded-lg shadow-soft p-6 mb-8">
                <h2 class="text-xl font-semibold mb-4 flex items-center">
                    <i class="fa fa-check-circle text-secondary mr-2"></i>处理完成
                </h2>
                
                <div class="grid md:grid-cols-4 gap-4 mb-6 text-center">
                    <div class="bg-blue-50 p-4 rounded-lg">
                        <p class="text-sm text-gray-600 mb-1">总文件数</p>
                        <p id="totalFiles" class="text-2xl font-bold text-primary">0</p>
                    </div>
                    <div class="bg-purple-50 p-4 rounded-lg">
                        <p class="text-sm text-gray-600 mb-1">总条目</p>
                        <p id="totalCount" class="text-2xl font-bold text-purple-600">0</p>
                    </div>
                    <div class="bg-green-50 p-4 rounded-lg">
                        <p class="text-sm text-gray-600 mb-1">有效条目</p>
                        <p id="validCount" class="text-2xl font-bold text-secondary">0</p>
                    </div>
                    <div class="bg-yellow-50 p-4 rounded-lg">
                        <p class="text-sm text-gray-600 mb-1">分组数</p>
                        <p id="groupsCount" class="text-2xl font-bold text-yellow-600">0</p>
                    </div>
                </div>
                
                <!-- 分组管理工具栏 -->
                <div class="flex flex-wrap gap-3 mb-6 p-3 bg-gray-50 rounded-lg">
                    <button id="toggleGroupView" class="inline-flex items-center bg-white border border-gray-300 text-gray-700 py-1 px-3 rounded-md hover:bg-gray-50 transition-all-300">
                        <i class="fa fa-th-list mr-1"></i>
                        <span id="toggleGroupViewText">列表视图</span>
                    </button>
                    
                    <div class="relative">
                        <input type="text" id="newGroupName" placeholder="新分组名称" class="pl-3 pr-24 py-1 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50">
                        <button id="createGroupBtn" class="absolute right-1 top-1 bg-primary text-white py-0 px-2 rounded text-sm hover:bg-primary/90 transition-all-300">
                            创建分组
                        </button>
                    </div>
                    
                    <button id="autoRegroupBtn" class="inline-flex items-center bg-primary/10 text-primary py-1 px-3 rounded-md hover:bg-primary/20 transition-all-300">
                        <i class="fa fa-refresh mr-1"></i>重新自动分组
                    </button>
                    
                    <div class="ml-auto">
                        <select id="moveToGroupSelect" class="py-1 px-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 bg-white">
                            <option value="">移动选中到...</option>
                        </select>
                    </div>
                </div>
                
                <!-- 预览区域 -->
                <div class="mb-6 border border-gray-200 rounded-lg overflow-hidden">
                    <div class="bg-gray-50 p-3 border-b border-gray-200">
                        <h3 class="font-medium flex items-center">
                            <i class="fa fa-play-circle text-primary mr-2"></i>在线预览
                        </h3>
                    </div>
                    <div class="p-4">
                        <div class="aspect-video bg-black rounded-md mb-4 relative overflow-hidden">
                            <video id="previewPlayer" class="w-full h-full object-contain" controls>
                                您的浏览器不支持视频播放
                            </video>
                            <div id="playerPlaceholder" class="absolute inset-0 flex items-center justify-center text-gray-400">
                                <i class="fa fa-film text-5xl"></i>
                            </div>
                        </div>
                        <div id="previewInfo" class="text-center text-gray-500 italic">
                            请从下方列表选择一个频道进行预览
                        </div>
                    </div>
                </div>
                
                <!-- 分组和频道列表 -->
                <div class="mb-6">
                    <div class="flex justify-between items-center mb-3">
                        <h3 class="font-medium flex items-center">
                            <i class="fa fa-list text-primary mr-2"></i>频道分组列表
                        </h3>
                        <div class="text-sm text-gray-500">
                            共 <span id="channelListCount">0</span> 个频道，分为 <span id="displayGroupsCount">0</span> 个分组
                        </div>
                    </div>
                    
                    <!-- 分组视图 -->
                    <div id="groupsView" class="border border-gray-200 rounded-lg max-h-80 overflow-y-auto scrollbar-thin">
                        <!-- 分组数据会动态填充在这里 -->
                    </div>
                    
                    <!-- 列表视图 (默认隐藏) -->
                    <div id="listView" class="hidden border border-gray-200 rounded-lg max-h-80 overflow-y-auto scrollbar-thin">
                        <table class="min-w-full divide-y divide-gray-200">
                            <thead class="bg-gray-50 sticky top-0">
                                <tr>
                                    <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                                        <input type="checkbox" id="selectAllChannels" class="form-checkbox text-primary">
                                    </th>
                                    <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">分组</th>
                                    <th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">频道名称</th>
                                    <th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
                                </tr>
                            </thead>
                            <tbody id="channelsTableBody" class="bg-white divide-y divide-gray-200">
                                <!-- 频道数据会动态填充在这里 -->
                            </tbody>
                        </table>
                    </div>
                </div>
                
                <div class="space-y-3">
                    <a id="validDownloadLink" href="#" class="flex items-center justify-center bg-secondary text-white py-2 px-4 rounded-md hover:bg-secondary/90 transition-all-300">
                        <i class="fa fa-download mr-2"></i>下载分组后的有效播放列表
                    </a>
                    
                    <a id="invalidDownloadLink" href="#" class="flex items-center justify-center bg-gray-200 text-gray-800 py-2 px-4 rounded-md hover:bg-gray-300 transition-all-300">
                        <i class="fa fa-download mr-2"></i>下载重复/无效条目列表
                    </a>
                </div>
                
                <button id="newProcessBtn" class="mt-6 w-full border border-primary text-primary py-2 px-4 rounded-md hover:bg-primary/5 transition-all-300">
                    处理新文件
                </button>
            </div>
        </div>
    </main>

    <!-- 页脚 -->
    <footer class="bg-dark text-white py-6">
        <div class="container mx-auto px-4 text-center text-sm text-gray-400">
            <p>M3U播放列表管理工具 &copy; 2023</p>
            <p class="mt-1">本工具仅用于个人学习和研究，请勿用于商业用途</p>
        </div>
    </footer>

    <script>
        // DOM元素
        const dropArea = document.getElementById('dropArea');
        const fileInput = document.getElementById('fileInput');
        const filesList = document.getElementById('filesList');
        const selectedFiles = document.getElementById('selectedFiles');
        const removeAllFiles = document.getElementById('removeAllFiles');
        const processBtn = document.getElementById('processBtn');
        const progressSection = document.getElementById('progressSection');
        const progressBar = document.getElementById('progressBar');
        const progressText = document.getElementById('progressText');
        const progressPercent = document.getElementById('progressPercent');
        const processLog = document.getElementById('processLog');
        const resultsSection = document.getElementById('resultsSection');
        const totalFiles = document.getElementById('totalFiles');
        const totalCount = document.getElementById('totalCount');
        const validCount = document.getElementById('validCount');
        const groupsCount = document.getElementById('groupsCount');
        const validDownloadLink = document.getElementById('validDownloadLink');
        const invalidDownloadLink = document.getElementById('invalidDownloadLink');
        const newProcessBtn = document.getElementById('newProcessBtn');
        const timeoutInput = document.getElementById('timeout');
        const retryInput = document.getElementById('retry');
        const previewPlayer = document.getElementById('previewPlayer');
        const playerPlaceholder = document.getElementById('playerPlaceholder');
        const previewInfo = document.getElementById('previewInfo');
        const channelsTableBody = document.getElementById('channelsTableBody');
        const channelListCount = document.getElementById('channelListCount');
        const displayGroupsCount = document.getElementById('displayGroupsCount');
        const groupsView = document.getElementById('groupsView');
        const listView = document.getElementById('listView');
        const toggleGroupView = document.getElementById('toggleGroupView');
        const toggleGroupViewText = document.getElementById('toggleGroupViewText');
        const newGroupName = document.getElementById('newGroupName');
        const createGroupBtn = document.getElementById('createGroupBtn');
        const autoRegroupBtn = document.getElementById('autoRegroupBtn');
        const moveToGroupSelect = document.getElementById('moveToGroupSelect');
        const selectAllChannels = document.getElementById('selectAllChannels');

        // 全局变量
        let selectedFilesArray = [];
        let allEntries = [];
        let validEntries = [];
        let invalidEntries = [];
        let duplicateEntries = [];
        let groups = {}; // 分组对象 {groupId: {name: '分组名', channels: [channelIds...]}}
        let groupIdCounter = 1;
        let hls = null;
        let isGroupView = true; // 默认显示分组视图

        // 初始化
        processBtn.disabled = true;

        // 事件监听 - 文件拖放
        ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
            dropArea.addEventListener(eventName, preventDefaults, false);
        });

        function preventDefaults(e) {
            e.preventDefault();
            e.stopPropagation();
        }

        ['dragenter', 'dragover'].forEach(eventName => {
            dropArea.addEventListener(eventName, highlight, false);
        });

        ['dragleave', 'drop'].forEach(eventName => {
            dropArea.addEventListener(eventName, unhighlight, false);
        });

        function highlight() {
            dropArea.classList.add('border-primary');
            dropArea.classList.add('bg-blue-50');
        }

        function unhighlight() {
            dropArea.classList.remove('border-primary');
            dropArea.classList.remove('bg-blue-50');
        }

        dropArea.addEventListener('drop', handleDrop, false);

        function handleDrop(e) {
            const dt = e.dataTransfer;
            const files = Array.from(dt.files);
            if (files.length > 0) {
                addFiles(files);
            }
        }

        // 事件监听 - 文件选择
        fileInput.addEventListener('change', function() {
            if (this.files.length > 0) {
                addFiles(Array.from(this.files));
            }
        });

        dropArea.addEventListener('click', function() {
            fileInput.click();
        });

        // 添加文件
        function addFiles(files) {
            let addedCount = 0;
            
            files.forEach(file => {
                // 检查文件格式
                if (!file.name.endsWith('.m3u') && !file.name.endsWith('.m3u8')) {
                    showLog(`跳过不支持的文件格式: ${file.name}`, 'error');
                    return;
                }
                
                // 检查是否已添加
                const isAlreadyAdded = selectedFilesArray.some(f => 
                    f.name === file.name && f.size === file.size && f.lastModified === file.lastModified
                );
                
                if (!isAlreadyAdded) {
                    selectedFilesArray.push(file);
                    addedCount++;
                    
                    // 显示在文件列表中
                    const fileItem = document.createElement('li');
                    fileItem.className = 'flex items-center justify-between p-2 bg-gray-50 rounded-md';
                    fileItem.dataset.id = `${file.name}-${file.size}-${file.lastModified}`;
                    fileItem.innerHTML = `
                        <div class="flex items-center">
                            <i class="fa fa-file-text-o text-primary mr-2"></i>
                            <span class="truncate max-w-[250px]">${file.name}</span>
                            <span class="ml-2 text-xs text-gray-500">(${formatFileSize(file.size)})</span>
                        </div>
                        <button class="remove-file text-gray-400 hover:text-danger transition-all-300" data-id="${file.name}-${file.size}-${file.lastModified}">
                            <i class="fa fa-times"></i>
                        </button>
                    `;
                    selectedFiles.appendChild(fileItem);
                    
                    // 添加删除单个文件的事件
                    fileItem.querySelector('.remove-file').addEventListener('click', function(e) {
                        const id = e.target.closest('.remove-file').dataset.id;
                        removeFileById(id);
                    });
                }
            });
            
            if (addedCount > 0) {
                filesList.classList.remove('hidden');
                processBtn.disabled = false;
                showLog(`已添加 ${addedCount} 个文件`);
            }
        }

        // 移除单个文件
        function removeFileById(id) {
            // 从数组中移除
            selectedFilesArray = selectedFilesArray.filter(file => 
                `${file.name}-${file.size}-${file.lastModified}` !== id
            );
            
            // 从DOM中移除
            const fileItem = document.querySelector(`li[data-id="${id}"]`);
            if (fileItem) {
                fileItem.remove();
            }
            
            // 如果没有文件了，隐藏文件列表
            if (selectedFilesArray.length === 0) {
                filesList.classList.add('hidden');
                processBtn.disabled = true;
            }
        }

        // 移除所有文件
        removeAllFiles.addEventListener('click', function() {
            selectedFilesArray = [];
            selectedFiles.innerHTML = '';
            filesList.classList.add('hidden');
            processBtn.disabled = true;
            showLog('已清空所有选中的文件');
        });

        // 格式化文件大小
        function formatFileSize(bytes) {
            if (bytes < 1024) return bytes + ' B';
            else if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
            else return (bytes / 1048576).toFixed(1) + ' MB';
        }

        // 开始处理
        processBtn.addEventListener('click', startProcessing);

        // 处理新文件
        newProcessBtn.addEventListener('click', function() {
            resultsSection.classList.add('hidden');
            dropArea.classList.remove('hidden');
            selectedFilesArray = [];
            selectedFiles.innerHTML = '';
            filesList.classList.add('hidden');
            processBtn.disabled = true;
            fileInput.value = '';
            allEntries = [];
            validEntries = [];
            invalidEntries = [];
            duplicateEntries = [];
            groups = {};
            groupIdCounter = 1;
            
            // 停止视频播放
            stopVideoPreview();
        });

        // 切换视图模式
        toggleGroupView.addEventListener('click', function() {
            isGroupView = !isGroupView;
            
            if (isGroupView) {
                groupsView.classList.remove('hidden');
                listView.classList.add('hidden');
                toggleGroupViewText.textContent = '列表视图';
                toggleGroupView.innerHTML = '<i class="fa fa-th-list mr-1"></i><span id="toggleGroupViewText">列表视图</span>';
            } else {
                groupsView.classList.add('hidden');
                listView.classList.remove('hidden');
                toggleGroupViewText.textContent = '分组视图';
                toggleGroupView.innerHTML = '<i class="fa fa-th-large mr-1"></i><span id="toggleGroupViewText">分组视图</span>';
            }
        });

        // 创建新分组
        createGroupBtn.addEventListener('click', function() {
            const groupName = newGroupName.value.trim();
            if (!groupName) return;
            
            const groupId = `group_${groupIdCounter++}`;
            groups[groupId] = {
                name: groupName,
                channels: []
            };
            
            newGroupName.value = '';
            updateGroupsUI();
            updateMoveToGroupSelect();
            showLog(`已创建新分组: ${groupName}`);
        });

        // 重新自动分组
        autoRegroupBtn.addEventListener('click', function() {
            const groupingMethod = document.querySelector('input[name="groupingMethod"]:checked').value;
            createGroupsFromChannels(groupingMethod);
            updateGroupsUI();
            updateMoveToGroupSelect();
            showLog('已重新自动分组');
        });

        // 移动选中频道到指定分组
        moveToGroupSelect.addEventListener('change', function() {
            const targetGroupId = this.value;
            if (!targetGroupId) return;
            
            // 获取所有选中的频道
            const selectedChannelIds = [];
            document.querySelectorAll('.channel-checkbox:checked').forEach(checkbox => {
                selectedChannelIds.push(checkbox.value);
            });
            
            if (selectedChannelIds.length === 0) {
                showLog('请先选择要移动的频道', 'error');
                return;
            }
            
            // 从原分组中移除
            Object.keys(groups).forEach(groupId => {
                groups[groupId].channels = groups[groupId].channels.filter(
                    channelId => !selectedChannelIds.includes(channelId)
                );
            });
            
            // 添加到目标分组
            groups[targetGroupId].channels = [...groups[targetGroupId].channels, ...selectedChannelIds];
            
            // 更新UI
            updateGroupsUI();
            showLog(`已将 ${selectedChannelIds.length} 个频道移动到分组: ${groups[targetGroupId].name}`);
            
            // 重置选择
            this.value = '';
            document.querySelectorAll('.channel-checkbox:checked').forEach(checkbox => {
                checkbox.checked = false;
            });
            selectAllChannels.checked = false;
        });

        // 全选/取消全选
        selectAllChannels.addEventListener('change', function() {
            const checked = this.checked;
            document.querySelectorAll('.channel-checkbox').forEach(checkbox => {
                checkbox.checked = checked;
            });
        });

        // 开始处理M3U文件
        function startProcessing() {
            if (selectedFilesArray.length === 0) return;

            // 重置变量
            allEntries = [];
            validEntries = [];
            invalidEntries = [];
            duplicateEntries = [];
            groups = {};
            groupIdCounter = 1;
            
            // 显示进度区域
            dropArea.classList.add('hidden');
            filesList.classList.add('hidden');
            progressSection.classList.remove('hidden');
            processLog.innerHTML = '';
            progressBar.style.width = '0%';
            progressPercent.textContent = '0%';
            progressText.textContent = '开始读取文件...';

            // 读取并解析所有文件
            readAndParseAllFiles(0);
        }

        // 读取并解析所有文件
        function readAndParseAllFiles(index) {
            if (index >= selectedFilesArray.length) {
                // 所有文件解析完成，开始检查重复
                setTimeout(() => {
                    checkDuplicates();
                }, 500);
                return;
            }

            const file = selectedFilesArray[index];
            progressText.textContent = `正在读取文件 ${index + 1}/${selectedFilesArray.length}: ${file.name}`;
            showLog(`读取文件: ${file.name}`);

            const reader = new FileReader();
            reader.onload = function(e) {
                const content = e.target.result;
                const entries = parseM3UContent(content, file.name);
                
                allEntries = allEntries.concat(entries);
                
                showLog(`解析完成 ${file.name}，发现 ${entries.length} 个播放条目`);
                
                // 继续处理下一个文件
                const progress = Math.round((index / selectedFilesArray.length) * 30); // 前30%进度用于读取文件
                progressBar.style.width = `${progress}%`;
                progressPercent.textContent = `${progress}%`;
                
                setTimeout(() => {
                    readAndParseAllFiles(index + 1);
                }, 100);
            };
            reader.readAsText(file);
        }

        // 解析M3U内容
        function parseM3UContent(content, fileName) {
            const lines = content.split('\n').map(line => line.trim());
            const entries = [];
            let currentInfLine = null;
            let lineNumber = 0;
            
            lines.forEach(line => {
                lineNumber++;
                if (line.startsWith('#EXTINF:')) {
                    currentInfLine = line;
                } else if (currentInfLine && (line.startsWith('http://') || line.startsWith('https://'))) {
                    // 从EXTINF行提取频道名称
                    let channelName = '未知频道';
                    const nameMatch = currentInfLine.match(/.+,(.+)$/);
                    if (nameMatch && nameMatch[1]) {
                        channelName = nameMatch[1].trim();
                    }
                    
                    // 生成唯一ID
                    const channelId = `channel_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
                    
                    entries.push({
                        id: channelId,
                        fileName,
                        lineNumber,
                        infLine: currentInfLine,
                        url: line,
                        channelName,
                        // 用于检测重复的基础URL（去除参数）
                        baseUrl: line.split('?')[0].split('#')[0],
                        // 提取可能的频道类型（用于自动分组）
                        possibleType: extractChannelType(channelName)
                    });
                    currentInfLine = null;
                } else if (line && !line.startsWith('#')) {
                    // 非标签行且非URL行，记录为无效
                    invalidEntries.push({
                        fileName,
                        lineNumber,
                        infLine: currentInfLine || '无',
                        url: line,
                        channelName: '无效条目',
                        reason: '非URL格式'
                    });
                    currentInfLine = null;
                }
            });
            
            return entries;
        }

        // 提取频道类型（用于自动分组）
        function extractChannelType(channelName) {
            const typeKeywords = [
                { type: '电影', keywords: ['电影', '影院', 'Movie', 'Cinema'] },
                { type: '体育', keywords: ['体育', '足球', '篮球', '赛事', 'Sports', 'Football', 'Basketball'] },
                { type: '新闻', keywords: ['新闻', 'News', '财经'] },
                { type: '娱乐', keywords: ['娱乐', '综艺', '音乐', 'Entertainment', 'Music'] },
                { type: '少儿', keywords: ['少儿', '卡通', '动画', '儿童', 'Kids', 'Cartoon'] },
                { type: '纪录片', keywords: ['纪录', 'Documentary'] },
                { type: '电视剧', keywords: ['剧集', '电视剧', 'TV Series', 'Drama'] }
            ];
            
            const lowerName = channelName.toLowerCase();
            
            for (const { type, keywords } of typeKeywords) {
                for (const keyword of keywords) {
                    if (lowerName.includes(keyword.toLowerCase())) {
                        return type;
                    }
                }
            }
            
            return '其他';
        }

        // 检查重复条目
        function checkDuplicates() {
            progressText.textContent = `检查重复条目 (共 ${allEntries.length} 个)`;
            showLog(`开始检查重复条目，总条目数: ${allEntries.length}`);
            
            const uniqueUrls = new Set();
            const duplicates = [];
            const keepDuplicateFirst = document.querySelector('input[name="duplicateAction"][value="keepFirst"]').checked;
            
            // 先按选择的策略处理重复项
            allEntries.forEach((entry, index) => {
                if (uniqueUrls.has(entry.baseUrl)) {
                    // 重复条目
                    duplicates.push(entry);
                    if (!keepDuplicateFirst) {
                        // 如果是保留最后一个，需要从有效列表中移除之前的条目
                        const prevIndex = allEntries.findIndex((e, i) => i < index && e.baseUrl === entry.baseUrl);
                        if (prevIndex !== -1) {
                            const prevEntry = allEntries[prevIndex];
                            // 将之前的条目移到重复列表
                            duplicates.push(prevEntry);
                            // 从有效列表中移除
                            allEntries.splice(prevIndex, 1);
                            index--; // 调整索引
                        }
                    }
                } else {
                    uniqueUrls.add(entry.baseUrl);
                }
            });
            
            duplicateEntries = duplicates;
            showLog(`重复检查完成，发现 ${duplicates.length} 个重复条目`);
            
            // 更新进度
            progressBar.style.width = '40%';
            progressPercent.textContent = '40%';
            
            // 开始检查URL有效性
            setTimeout(() => {
                checkUrls(0);
            }, 1000);
        }

        // 检查URL有效性
        function checkUrls(index) {
            if (index >= allEntries.length) {
                // 所有URL检查完成
                completeProcessing();
                return;
            }

            const entry = allEntries[index];
            const progress = 40 + Math.round((index / allEntries.length) * 60); // 剩余60%进度用于检查URL
            progressBar.style.width = `${progress}%`;
            progressPercent.textContent = `${progress}%`;
            progressText.textContent = `正在检查 ${index + 1}/${allEntries.length}: ${entry.channelName}`;
            showLog(`检查地址 ${index + 1}/${allEntries.length}: ${entry.channelName} - ${entry.url.substring(0, 50)}...`);

            // 模拟URL检查（实际应用中应该使用真实的API检查）
            const timeout = parseInt(timeoutInput.value) * 1000;
            const retry = parseInt(retryInput.value);
            
            // 随机模拟结果（实际应用中应替换为真实的URL检查）
            const isRandomlyValid = Math.random() > 0.3; // 70%概率有效
            
            setTimeout(() => {
                if (isRandomlyValid) {
                    validEntries.push(entry);
                    showLog(`地址有效: ${entry.channelName}`, 'success');
                } else {
                    // 尝试修复
                    const fixedUrl = attemptFixUrl(entry.url);
                    if (fixedUrl) {
                        validEntries.push({...entry, url: fixedUrl, baseUrl: fixedUrl.split('?')[0].split('#')[0]});
                        showLog(`地址已修复: ${entry.channelName}`, 'success');
                    } else {
                        invalidEntries.push({...entry, reason: '无法连接或修复'});
                        showLog(`地址无效: ${entry.channelName}`, 'error');
                    }
                }
                
                // 检查下一个URL
                checkUrls(index + 1);
            }, 600); // 模拟网络请求延迟
        }

        // 尝试修复URL
        function attemptFixUrl(url) {
            // 尝试常见的修复方法
            const fixes = [
                u => u.replace("http://", "https://"),
                u => u.replace("https://", "http://"),
                u => u.split('?')[0],
                u => u.split('#')[0]
            ];
            
            // 随机选择一个修复方法作为演示（实际应用中应验证修复后的URL）
            const randomFix = fixes[Math.floor(Math.random() * fixes.length)];
            const fixedUrl = randomFix(url);
            
            // 50%概率修复成功
            return Math.random() > 0.5 ? fixedUrl : null;
        }

        // 创建频道分组
        function createGroupsFromChannels(method) {
            groups = {};
            groupIdCounter = 1;
            
            if (method === 'none') {
                // 不分组，创建一个"全部频道"分组
                const groupId = `group_${groupIdCounter++}`;
                groups[groupId] = {
                    name: '全部频道',
                    channels: validEntries.map(channel => channel.id)
                };
                return;
            }
            
            // 根据不同方法创建分组
            validEntries.forEach(channel => {
                let groupName;
                
                if (method === 'channelType') {
                    // 按频道类型分组
                    groupName = channel.possibleType || '其他';
                } else if (method === 'sourceFile') {
                    // 按源文件分组
                    groupName = `来源: ${channel.fileName}`;
                }
                
                // 查找或创建分组
                let groupId = Object.keys(groups).find(id => groups[id].name === groupName);
                
                if (!groupId) {
                    groupId = `group_${groupIdCounter++}`;
                    groups[groupId] = {
                        name: groupName,
                        channels: []
                    };
                }
                
                // 添加频道到分组
                groups[groupId].channels.push(channel.id);
            });
        }

        // 更新分组UI
        function updateGroupsUI() {
            // 清空现有内容
            groupsView.innerHTML = '';
            channelsTableBody.innerHTML = '';
            
            const groupIds = Object.keys(groups);
            groupsCount.textContent = groupIds.length;
            displayGroupsCount.textContent = groupIds.length;
            
            // 按分组名称排序
            groupIds.sort((a, b) => groups[a].name.localeCompare(groups[b].name));
            
            // 生成分组视图
            groupIds.forEach(groupId => {
                const group = groups[groupId];
                const groupChannels = validEntries.filter(channel => 
                    group.channels.includes(channel.id)
                );
                
                // 分组容器
                const groupElement = document.createElement('div');
                groupElement.className = 'border-b border-gray-200 last:border-0';
                
                // 分组标题
                const groupHeader = document.createElement('div');
                groupHeader.className = 'group-header px-4 py-2 flex justify-between items-center cursor-pointer';
                groupHeader.innerHTML = `
                    <div class="font-medium flex items-center">
                        <i class="fa fa-folder-open text-primary mr-2"></i>
                        <span>${group.name}</span>
                        <span class="ml-2 text-xs text-gray-500">(${groupChannels.length}个频道)</span>
                    </div>
                    <div class="flex items-center space-x-2">
                        <button class="rename-group text-gray-400 hover:text-primary transition-all-300 text-sm" data-group-id="${groupId}">
                            <i class="fa fa-pencil"></i>
                        </button>
                        <button class="delete-group text-gray-400 hover:text-danger transition-all-300 text-sm" data-group-id="${groupId}">
                            <i class="fa fa-trash-o"></i>
                        </button>
                        <button class="toggle-group text-gray-400 hover:text-primary transition-all-300 text-sm" data-group-id="${groupId}">
                            <i class="fa fa-chevron-down"></i>
                        </button>
                    </div>
                `;
                groupElement.appendChild(groupHeader);
                
                // 频道列表
                const channelsContainer = document.createElement('div');
                channelsContainer.className = 'px-4 py-2';
                channelsContainer.dataset.groupId = groupId;
                
                if (groupChannels.length === 0) {
                    channelsContainer.innerHTML = '<div class="text-gray-500 italic text-sm py-2">该分组没有频道</div>';
                } else {
                    // 按频道名称排序
                    groupChannels.sort((a, b) => a.channelName.localeCompare(b.channelName));
                    
                    groupChannels.forEach(channel => {
                        const channelElement = document.createElement('div');
                        channelElement.className = 'channel-item flex justify-between items-center py-2 border-b border-gray-100 last:border-0';
                        channelElement.innerHTML = `
                            <div class="flex items-center">
                                <i class="fa fa-television text-primary mr-2"></i>
                                <span class="truncate max-w-[300px]">${channel.channelName}</span>
                            </div>
                            <button class="preview-channel text-primary hover:text-primary/80 transition-all-300 text-sm" 
                                    data-url="${channel.url}" 
                                    data-name="${channel.channelName}">
                                <i class="fa fa-play mr-1"></i>预览
                            </button>
                        `;
                        channelsContainer.appendChild(channelElement);
                        
                        // 添加预览事件
                        channelElement.querySelector('.preview-channel').addEventListener('click', function(e) {
                            const url = e.currentTarget.dataset.url;
                            const name = e.currentTarget.dataset.name;
                            previewChannel(url, name);
                        });
                    });
                }
                
                groupElement.appendChild(channelsContainer);
                groupsView.appendChild(groupElement);
                
                // 添加分组折叠/展开功能
                groupHeader.querySelector('.toggle-group').addEventListener('click', function() {
                    const icon = this.querySelector('i');
                    if (icon.classList.contains('fa-chevron-down')) {
                        icon.classList.replace('fa-chevron-down', 'fa-chevron-right');
                        channelsContainer.classList.add('hidden');
                    } else {
                        icon.classList.replace('fa-chevron-right', 'fa-chevron-down');
                        channelsContainer.classList.remove('hidden');
                    }
                });
                
                // 添加重命名分组功能
                groupHeader.querySelector('.rename-group').addEventListener('click', function() {
                    const newName = prompt('请输入新的分组名称:', group.name);
                    if (newName && newName.trim() && newName !== group.name) {
                        groups[groupId].name = newName.trim();
                        updateGroupsUI();
                        updateMoveToGroupSelect();
                        showLog(`已将分组重命名为: ${newName.trim()}`);
                    }
                });
                
                // 添加删除分组功能
                groupHeader.querySelector('.delete-group').addEventListener('click', function() {
                    if (groupChannels.length > 0 && !confirm(`确定要删除分组"${group.name}"吗？该分组中的${groupChannels.length}个频道将被移至"未分组"。`)) {
                        return;
                    }
                    
                    // 检查是否已有"未分组"
                    let ungroupedId = Object.keys(groups).find(id => groups[id].name === '未分组');
                    
                    if (!ungroupedId) {
                        ungroupedId = `group_${groupIdCounter++}`;
                        groups[ungroupedId] = {
                            name: '未分组',
                            channels: []
                        };
                    }
                    
                    // 移动频道到"未分组"
                    groups[ungroupedId].channels = [...groups[ungroupedId].channels, ...group.channels];
                    
                    // 删除分组
                    delete groups[groupId];
                    
                    updateGroupsUI();
                    updateMoveToGroupSelect();
                    showLog(`已删除分组: ${group.name}`);
                });
            });
            
            // 生成列表视图
            // 按频道名称排序所有有效频道
            const sortedChannels = [...validEntries].sort((a, b) => a.channelName.localeCompare(b.channelName));
            
            sortedChannels.forEach(channel => {
                // 找到频道所属的分组
                let channelGroup = '未分组';
                Object.keys(groups).forEach(groupId => {
                    if (groups[groupId].channels.includes(channel.id)) {
                        channelGroup = groups[groupId].name;
                    }
                });
                
                const row = document.createElement('tr');
                row.className = 'hover:bg-gray-50 transition-all-300';
                row.innerHTML = `
                    <td class="px-4 py-3 whitespace-nowrap">
                        <input type="checkbox" class="channel-checkbox form-checkbox text-primary" value="${channel.id}">
                    </td>
                    <td class="px-4 py-3 whitespace-nowrap">
                        <div class="text-sm text-gray-900">${channelGroup}</div>
                    </td>
                    <td class="px-4 py-3 whitespace-nowrap">
                        <div class="text-sm font-medium text-gray-900">${channel.channelName}</div>
                        <div class="text-xs text-gray-500 truncate max-w-[300px]">${channel.url}</div>
                    </td>
                    <td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
                        <button class="preview-channel text-primary hover:text-primary/80 transition-all-300" 
                                data-url="${channel.url}" 
                                data-name="${channel.channelName}">
                            <i class="fa fa-play mr-1"></i>预览
                        </button>
                    </td>
                `;
                channelsTableBody.appendChild(row);
                
                // 添加预览事件
                row.querySelector('.preview-channel').addEventListener('click', function(e) {
                    const url = e.currentTarget.dataset.url;
                    const name = e.currentTarget.dataset.name;
                    previewChannel(url, name);
                });
            });
        }

        // 更新移动到分组下拉菜单
        function updateMoveToGroupSelect() {
            // 保存当前选中值
            const currentValue = moveToGroupSelect.value;
            
            // 清空现有选项
            moveToGroupSelect.innerHTML = '<option value="">移动选中到...</option>';
            
            // 添加所有分组
            Object.keys(groups).forEach(groupId => {
                const group = groups[groupId];
                const option = document.createElement('option');
                option.value = groupId;
                option.textContent = group.name;
                moveToGroupSelect.appendChild(option);
            });
            
            // 恢复选中值（如果仍然存在）
            if (currentValue && Object.keys(groups).includes(currentValue)) {
                moveToGroupSelect.value = currentValue;
            }
        }

        // 完成处理
        function completeProcessing() {
            progressBar.style.width = '100%';
            progressPercent.textContent = '100%';
            progressText.textContent = '处理完成';
            showLog('所有地址检查完成');
            
            // 更新统计信息
            totalFiles.textContent = selectedFilesArray.length;
            totalCount.textContent = allEntries.length + invalidEntries.length;
            validCount.textContent = validEntries.length;
            channelListCount.textContent = validEntries.length;
            
            // 创建分组
            const groupingMethod = document.querySelector('input[name="groupingMethod"]:checked').value;
            createGroupsFromChannels(groupingMethod);
            
            // 生成UI
            updateGroupsUI();
            updateMoveToGroupSelect();
            
            // 生成下载文件
            generateDownloadFiles();
            
            // 显示结果区域
            setTimeout(() => {
                progressSection.classList.add('hidden');
                resultsSection.classList.remove('hidden');
            }, 1500);
        }

        // 预览频道
        function previewChannel(url, name) {
            // 停止当前播放
            stopVideoPreview();
            
            previewInfo.textContent = `正在加载: ${name}`;
            playerPlaceholder.style.display = 'none';
            previewPlayer.style.display = 'block';
            
            // 检查是否是HLS流
            if (url.indexOf('.m3u8') !== -1) {
                // 检查浏览器是否原生支持HLS
                if (previewPlayer.canPlayType('application/vnd.apple.mpegurl')) {
                    // 原生支持
                    previewPlayer.src = url;
                    previewPlayer.addEventListener('loadedmetadata', () => {
                        previewInfo.textContent = `正在播放: ${name}`;
                        previewPlayer.play();
                    });
                } else if (Hls.isSupported()) {
                    // 使用hls.js库
                    hls = new Hls();
                    hls.loadSource(url);
                    hls.attachMedia(previewPlayer);
                    hls.on(Hls.Events.MANIFEST_PARSED, () => {
                        previewInfo.textContent = `正在播放: ${name}`;
                        previewPlayer.play();
                    });
                    hls.on(Hls.Events.ERROR, (event, data) => {
                        if (data.fatal) {
                            switch(data.type) {
                                case Hls.ErrorTypes.NETWORK_ERROR:
                                    previewInfo.textContent = `网络错误，无法播放: ${name}`;
                                    hls.startLoad();
                                    break;
                                case Hls.ErrorTypes.MEDIA_ERROR:
                                    previewInfo.textContent = `媒体错误，无法播放: ${name}`;
                                    hls.recoverMediaError();
                                    break;
                                default:
                                    previewInfo.textContent = `无法播放: ${name}`;
                                    destroyHls();
                                    break;
                            }
                        }
                    });
                } else {
                    previewInfo.textContent = `您的浏览器不支持HLS播放: ${name}`;
                }
            } else {
                // 非HLS流，直接播放
                previewPlayer.src = url;
                previewPlayer.addEventListener('loadedmetadata', () => {
                    previewInfo.textContent = `正在播放: ${name}`;
                    previewPlayer.play();
                });
                previewPlayer.addEventListener('error', () => {
                    previewInfo.textContent = `无法播放: ${name}`;
                });
            }
        }

        // 停止视频预览
        function stopVideoPreview() {
            if (hls) {
                destroyHls();
            }
            previewPlayer.pause();
            previewPlayer.src = '';
            playerPlaceholder.style.display = 'flex';
            previewInfo.textContent = '请从下方列表选择一个频道进行预览';
        }

        // 销毁HLS实例
        function destroyHls() {
            if (hls) {
                hls.destroy();
                hls = null;
            }
        }

        // 生成下载文件
        function generateDownloadFiles() {
            // 生成带分组的有效播放列表
            let validContent = "#EXTM3U\n";
            
            // 按分组顺序
            Object.keys(groups).forEach(groupId => {
                const group = groups[groupId];
                const groupChannels = validEntries.filter(channel => 
                    group.channels.includes(channel.id)
                );
                
                if (groupChannels.length > 0) {
                    // 添加分组标记（M3U扩展格式）
                    validContent += `#EXTGRP:${group.name}\n`;
                    
                    // 添加该分组下的所有频道
                    groupChannels.forEach(entry => {
                        validContent += `${entry.infLine}\n`;
                        validContent += `${entry.url}\n`;
                    });
                }
            });
            
            // 生成无效/重复条目列表
            let invalidContent = "#EXTM3U\n";
            invalidContent += "# 无效条目\n";
            invalidEntries.forEach(entry => {
                invalidContent += `# 来源: ${entry.fileName} (行${entry.lineNumber}) 原因: ${entry.reason}\n`;
                invalidContent += `${entry.infLine}\n`;
                invalidContent += `${entry.url}\n`;
            });
            
            invalidContent += "\n# 重复条目\n";
            duplicateEntries.forEach(entry => {
                invalidContent += `# 来源: ${entry.fileName} (行${entry.lineNumber}) 原因: 重复地址\n`;
                invalidContent += `${entry.infLine}\n`;
                invalidContent += `${entry.url}\n`;
            });
            
            // 创建下载链接
            const date = new Date().toISOString().split('T')[0];
            
            createDownloadLink(
                validDownloadLink, 
                validContent, 
                `grouped_valid_${date}.m3u`
            );
            
            if (invalidEntries.length > 0 || duplicateEntries.length > 0) {
                createDownloadLink(
                    invalidDownloadLink, 
                    invalidContent, 
                    `invalid_duplicate_${date}.m3u`
                );
            } else {
                invalidDownloadLink.classList.add('hidden');
            }
        }

        // 创建下载链接
        function createDownloadLink(linkElement, content, filename) {
            const blob = new Blob([content], { type: 'application/x-mpegURL' });
            const url = URL.createObjectURL(blob);
            
            linkElement.href = url;
            linkElement.download = filename;
            linkElement.classList.remove('hidden');
        }

        // 显示日志
        function showLog(message, type = 'info') {
            const logElement = document.createElement('div');
            logElement.className = `mb-1 ${
                type === 'success' ? 'text-green-600' : 
                type === 'error' ? 'text-red-600' : 'text-gray-700'
            }`;
            
            const time = new Date().toLocaleTimeString();
            logElement.innerHTML = `[${time}] ${message}`;
            
            processLog.appendChild(logElement);
            processLog.scrollTop = processLog.scrollHeight;
        }

        // 页面关闭时清理
        window.addEventListener('beforeunload', () => {
            stopVideoPreview();
        });
    </script>
</body>
</html>
    